﻿using System;
using System.IO;
using BrainTechLLC.ThreadSafeObjects;
using BrainTechLLC;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;

namespace WpfImageDuplicates
{
    [Serializable]
    public class SavedMetaData
    {
        private List<double> _ratings;

        public string FileName { get; set; }
        
        public List<double> Ratings
        {
            get
            {
                if (_ratings == null)
                    Interlocked.CompareExchange(ref _ratings, new List<double>(), null);

                return _ratings;
            }
            set
            {
                _ratings = value;
            }
        }
    }

    [Serializable]
    public class AllMetaData : ThreadSafeLookup<string, SavedMetaData>
    {
        [NonSerialized]
        public bool _dirty;

        public void SaveIfDirty(string path)
        {
            if (_dirty)
            {
                _dirty = false;
                SaveToDisk(path);
            }
        }

        public void SaveToDisk(string path)
        {
            File.WriteAllBytes(path, this.SerializeToCompressedBinary());
        }

        public SavedMetaData GetMetaData(string fileName)
        {
            SavedMetaData found = this[fileName];
            
            if (found == null)
            {
                found = new SavedMetaData() { FileName = fileName };
                this.AddOrSet(fileName, found);
                _dirty = true;
            }

            return found;
        }

        public void AddMetaData(string fileName, double rating)
        {
            SavedMetaData found = GetMetaData(fileName);
            found.Ratings.Add(rating);
            Console.WriteLine(string.Format("Added rating of {0} to file {1}", rating, fileName));
            _dirty = true;
        }

        public static AllMetaData ReadFromDisk(string path)
        {
            byte[] bytes = File.ReadAllBytes(path);
            AllMetaData result = bytes.DeserializeFromCompressedBinary() as AllMetaData;
            return result;
        }
    }

    public static class Serialization
    {
        #region Methods

        public static object DeserializeFromBinary(this MemoryStream ms)
        {
            return GetFormatter().Deserialize(ms);
        }

        public static object DeserializeFromBinary(this MemoryStream ms, IFormatter bf)
        {
            return bf.Deserialize(ms);
        }

        public static object DeserializeFromBinary(this FileStream ms)
        {
            return GetFormatter().Deserialize(ms);
        }

        public static object DeserializeFromCompressedBinary(this MemoryStream ms)
        {
            using (MemoryStream m = new MemoryStream(MiniLZO.Decompress(ms.GetBuffer())))
            {
                m.Seek(0, SeekOrigin.Begin);
                return m.DeserializeFromBinary();
            }
        }

        public static object DeserializeFromCompressedBinary(this byte[] data)
        {
            object o = null;
            byte[] decompressed = MiniLZO.Decompress(data);
            using (MemoryStream ms = new MemoryStream(decompressed))
            {
                ms.Seek(0, SeekOrigin.Begin);
                try
                {
                    o = ms.DeserializeFromBinary();
                }
                catch { o = null; }
                ms.Close();
            }
            return o;
        }

        public static object DeserializeFromBinary(this byte[] data)
        {
            object o = null;
            using (MemoryStream ms = new MemoryStream(data))
            {
                ms.Seek(0, SeekOrigin.Begin);
                try
                {
                    o = ms.DeserializeFromBinary();
                }
                catch { o = null; }
                ms.Close();
            }
            return o;
        }

        public static object DeserializeFromCompressedBinary(this FileStream fs)
        {
            byte[] data;
            long len = fs.Length;

            using (BinaryReader br = new BinaryReader(fs))
            {
                data = br.ReadBytes(Convert.ToInt32(len));
            }

            return data.DeserializeFromCompressedBinary();
        }

        public static byte[] SerializeToBinary(this object o)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                GetFormatter().Serialize(ms, o);
                return ms.ToArray();
            }
        }

        public static void SerializeToBinaryMemoryStream(this object o, MemoryStream ms)
        {
            GetFormatter().Serialize(ms, o);
        }

        public static byte[] SerializeToCompressedBinary(this object o)
        {
            byte[] data;
            using (MemoryStream ms = new MemoryStream())
            {
                try
                {
                    GetFormatter().Serialize(ms, o);
                }
                catch { throw; }
                data = ms.ToArray();
                return MiniLZO.Compress(data);
            }
        }

        public static void SerializeToCompressedBinaryMemoryStream(this object o, MemoryStream ms)
        {
            byte[] data = o.SerializeToCompressedBinary();
            ms.Write(data, 0, data.Length);
        }

        private static IFormatter GetFormatter()
        {
            return new BinaryFormatter();
        }

        #endregion Methods
    }
}

